package net.qiqbframework.messaging.annotation;

import net.qiqbframework.messaging.Message;

import javax.annotation.Nonnull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptySet;
import static java.util.Collections.emptySortedSet;

/**
 * 标记 {@link MessageHandler} 注解方法或者构造器，将解析为对应的 MessageHandlingMember
 */
public class AnnotatedHandlerInspector<T> {

    private final Class<T> inspectedType;
    private final HandlerParameterResolverFactory parameterResolverFactory;
    private final Map<Class<?>, AnnotatedHandlerInspector<?>> registry;
    private final List<AnnotatedHandlerInspector<? super T>> superClassInspectors;
    private final List<AnnotatedHandlerInspector<? extends T>> subClassInspectors;
    private final Map<Class<?>, SortedSet<MessageExecutableHandler>> handlers;
    private final MessageHandlingMemberFactory handlerDefinition;
    private final Map<Class<?>, MessageHandlerInterceptorMemberChain<T>> interceptorChains;
    private final Map<Class<?>, SortedSet<MessageExecutableHandler>> interceptors;

    private AnnotatedHandlerInspector(Class<T> inspectedType,
                                      List<AnnotatedHandlerInspector<? super T>> superClassInspectors,
                                      HandlerParameterResolverFactory parameterResolverFactory,
                                      MessageHandlingMemberFactory handlerDefinition,
                                      Map<Class<?>, AnnotatedHandlerInspector<?>> registry,
                                      List<AnnotatedHandlerInspector<? extends T>> subClassInspectors) {
        this.inspectedType = inspectedType;
        this.parameterResolverFactory = parameterResolverFactory;
        this.registry = registry;
        this.superClassInspectors = new ArrayList<>(superClassInspectors);
        this.handlers = new HashMap<>();
        this.handlerDefinition = handlerDefinition;
        this.subClassInspectors = subClassInspectors;
        this.interceptorChains = new ConcurrentHashMap<>();
        this.interceptors = new ConcurrentHashMap<>();
    }



    public static <T> AnnotatedHandlerInspector<T> inspectType(Class<? extends T> handlerType,
                                                               HandlerParameterResolverFactory parameterResolverFactory,
                                                               HandlerAttributesPostProcessor handlerAttributesPostProcessor,
                                                               MessageHandlingMemberFactory handlerDefinition) {
        return inspectType(handlerType, parameterResolverFactory,handlerAttributesPostProcessor, handlerDefinition, emptySet());
    }


    public static <T> AnnotatedHandlerInspector<T> inspectType(Class<? extends T> handlerType,
                                                               HandlerParameterResolverFactory parameterResolverFactory,
                                                               HandlerAttributesPostProcessor handlerAttributesPostProcessor,
                                                               MessageHandlingMemberFactory handlerDefinition,
                                                               Set<Class<? extends T>> declaredSubtypes) {
        return createInspector(handlerType,
                parameterResolverFactory,
                handlerAttributesPostProcessor,
                handlerDefinition,
                new HashMap<>(),
                declaredSubtypes);
    }

    @SuppressWarnings("unchecked")
    private static <T> AnnotatedHandlerInspector<T> createInspector(Class<? extends T> inspectedType,
                                                                    HandlerParameterResolverFactory parameterResolverFactory,
                                                                    HandlerAttributesPostProcessor handlerAttributesPostProcessor,
                                                                    MessageHandlingMemberFactory handlerDefinition,
                                                                    Map<Class<?>, AnnotatedHandlerInspector<?>> registry,
                                                                    Set<Class<? extends T>> declaredSubtypes) {
        if (!registry.containsKey(inspectedType)) {
            registry.put(inspectedType,
                    AnnotatedHandlerInspector.initialize((Class<T>) inspectedType,
                            parameterResolverFactory,
                            handlerAttributesPostProcessor,
                            handlerDefinition,
                            registry,
                            declaredSubtypes));
        }
        // noinspection unchecked
        return (AnnotatedHandlerInspector<T>) registry.get(inspectedType);
    }

    private static <T> AnnotatedHandlerInspector<T> initialize(Class<T> inspectedType,
                                                               HandlerParameterResolverFactory parameterResolverFactory,
                                                               HandlerAttributesPostProcessor handlerAttributesPostProcessor,
                                                               MessageHandlingMemberFactory handlerDefinition,
                                                               Map<Class<?>, AnnotatedHandlerInspector<?>> registry,
                                                               Set<Class<? extends T>> declaredSubtypes) {
        List<AnnotatedHandlerInspector<? super T>> parents = new ArrayList<>();
        for (Class<?> iFace : inspectedType.getInterfaces()) {
            parents.add(createInspector(iFace,
                    parameterResolverFactory,
                    handlerAttributesPostProcessor,
                    handlerDefinition,
                    registry,
                    emptySet()));
        }
        if (inspectedType.getSuperclass() != null && !Object.class.equals(inspectedType.getSuperclass())) {
            parents.add(createInspector(inspectedType.getSuperclass(),
                    parameterResolverFactory,
                    handlerAttributesPostProcessor,
                    handlerDefinition,
                    registry,
                    emptySet()));
        }
        List<AnnotatedHandlerInspector<? extends T>> children =
                declaredSubtypes.stream()
                        .map(subclass -> createInspector(subclass,
                                parameterResolverFactory,
                                handlerAttributesPostProcessor,
                                handlerDefinition,
                                registry,
                                emptySet()))
                        .collect(Collectors.toList());
        AnnotatedHandlerInspector<T> inspector = new AnnotatedHandlerInspector<>(inspectedType,
                parents,
                parameterResolverFactory,
                handlerDefinition,
                registry,
                children);
        inspector.initializeMessageHandlers(parameterResolverFactory, handlerDefinition,handlerAttributesPostProcessor);
        return inspector;
    }

    @SuppressWarnings("unchecked")
    private void initializeMessageHandlers(HandlerParameterResolverFactory parameterResolverFactory,
                                           MessageHandlingMemberFactory handlerDefinition
            , @Nonnull HandlerAttributesPostProcessor handlerAttributesPostProcessor) {
        handlers.put(inspectedType, new TreeSet<>(HandlerComparator.instance()));
        for (Method method : inspectedType.getDeclaredMethods()) {
            handlerDefinition.createHandler(inspectedType, method, parameterResolverFactory,handlerAttributesPostProcessor)
                    .ifPresent(h -> registerHandler(inspectedType, h));
        }
        for (Constructor<?> constructor : inspectedType.getDeclaredConstructors()) {
            handlerDefinition.createHandler(inspectedType, constructor, parameterResolverFactory,handlerAttributesPostProcessor)
                    .ifPresent(h -> registerHandler(inspectedType, h));
        }

        // we need to consider handlers from parent/subclasses as well
        subClassInspectors.forEach(sci -> sci.getAllHandlers()
                .forEach((key, value) -> value.forEach(h -> registerHandler(key, (MessageExecutableHandler) h))));
        superClassInspectors.forEach(sci -> sci.getAllHandlers()
                .forEach((key, value) -> value.forEach(h -> {
                    boolean isAbstract = h.unwrap(Executable.class)
                            .map(e -> Modifier.isAbstract(e.getModifiers()))
                            .orElse(false);
                    if (!isAbstract) {
                        registerHandler(key, h);
                    }
                    registerHandler(inspectedType, h);
                })));

        // we need to consider interceptors from parent/subclasses as well
        subClassInspectors.forEach(sci -> sci.getAllInterceptors()
                .forEach((key, value) -> value.forEach(h -> registerHandler(key, (MessageExecutableHandler) h))));
        superClassInspectors.forEach(sci -> sci.getAllInterceptors()
                .forEach((key, value) -> value.forEach(h -> {
                    registerHandler(key, h);
                    registerHandler(inspectedType, h);
                })));
    }

    private void registerHandler(Class<?> type, MessageExecutableHandler handler) {
        if (handler.unwrap(InterceptingMember.class).isPresent()) {
            interceptors.computeIfAbsent(type, t -> new TreeSet<>(HandlerComparator.instance()))
                    .add(handler);

        } else {
            handlers.computeIfAbsent(type, t -> new TreeSet<>(HandlerComparator.instance()))
                    .add(handler);
        }
    }

    public <C> AnnotatedHandlerInspector<C> inspect(Class<? extends C> entityType) {
        return AnnotatedHandlerInspector.createInspector(entityType,
                parameterResolverFactory,
                HandlerAttributesPostProcessor.EMPTY,
                handlerDefinition,
                registry,
                emptySet());
    }

    
    public Stream<MessageExecutableHandler> getHandlers(Class<?> type) {
        return handlers.getOrDefault(type, emptySortedSet())
                .stream();
    }

    public MessageHandlerInterceptorMemberChain<T> chainedInterceptor(Class<?> type) {
        return interceptorChains.computeIfAbsent(type, t -> {
            Collection<MessageExecutableHandler> i = interceptors.getOrDefault(type, emptySortedSet());
            if (i.isEmpty()) {
                return NoMoreInterceptors.instance();
            }
            return new ChainedMessageHandlerInterceptorMember<>(t, i.iterator());
        });
    }

    /**
     * 获取所有序的 handler
     */
    public Map<Class<?>, SortedSet<MessageExecutableHandler>> getAllHandlers() {
        return Collections.unmodifiableMap(handlers);
    }

    public Map<Class<?>, SortedSet<MessageExecutableHandler>> getAllInterceptors() {
        return Collections.unmodifiableMap(interceptors);
    }


    public Set<Class<?>> getAllInspectedTypes() {
        Set<Class<?>> inspectedTypes = new HashSet<>();
        inspectedTypes.add(inspectedType);
        subClassInspectors.stream()
                .map(AnnotatedHandlerInspector::getAllInspectedTypes)
                .forEach(inspectedTypes::addAll);
        superClassInspectors.stream()
                .map(AnnotatedHandlerInspector::getAllInspectedTypes)
                .forEach(inspectedTypes::addAll);
        return Collections.unmodifiableSet(inspectedTypes);
    }

    private static class ChainedMessageHandlerInterceptorMember<T> implements MessageHandlerInterceptorMemberChain<T> {
        private final MessageExecutableHandler delegate;
        private final MessageHandlerInterceptorMemberChain<T> next;

        private ChainedMessageHandlerInterceptorMember(Class<?> targetType, Iterator<MessageExecutableHandler> iterator) {
            this.delegate = iterator.next();
            if (iterator.hasNext()) {
                this.next = new ChainedMessageHandlerInterceptorMember<>(targetType, iterator);
            } else {
                this.next = NoMoreInterceptors.instance();
            }
        }

        @Override
        public Object handle(@Nonnull Message<?> message, @Nonnull T target,
                             @Nonnull MessageExecutableHandler handler) throws Exception {
            return InterceptorChainParameterResolverFactory.callWithInterceptorChain(() -> next.handle(message,
                            target,
                            handler),
                    () -> doHandle(message,
                            target,
                            handler));
        }

        private Object doHandle(Message<?> message, T target, MessageExecutableHandler handler) throws Exception {
            if (delegate.canHandle(message)) {
                return delegate.handle(message, target);
            }
            return next.handle(message, target, handler);
        }
    }
}
